/**
* (C) Tody 2014.
* This code is open source, and it's for Intel CMPC widevine keybox function.
* Function: 
*	generate keybox binary file of google format(keybox.xml).
* Version: 1.0.0
* Author: Tody Guo
* Date: 2014-09-10
* Email: tody_guo@live.com
*/

#include <iostream>
#include "tinyxml.h"
#include "tinystr.h"
#include <stdio.h>
#include <string.h>

#define DEBUG 0

typedef struct _wvbox
{
	char DeviceId[32];
	unsigned char Key[16];
	unsigned char ID[72];
	unsigned char Magic[4];
	unsigned char CRC[4];
}WVBOX;


void help(void)
{
	fprintf(stderr, "(c) Tody, 2014  -version 1.0.0\n");	
	fprintf(stderr, "Usage:\n");
	fprintf(stderr, "   wvBox.exe -file <keybox XML File> -key <KeyID>  -bin <BIN File>\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "   -key <KeyID>\tDeviceID from keybox.xml\n");
	fprintf(stderr, "   -file <File>\tRead file from xml file specified\n");
	fprintf(stderr, "   -bin <File>\tgenerate bin file specified\n");
	fprintf(stderr, "\nReturn code:\n");
	fprintf(stderr, "   Pass(0)\t Fail(1)\n");
	exit(1);
}

int charToHex(char ch)
{
	switch(ch){
		case '0':	return 0; break;
		case '1':	return 1; break;
		case '2':	return 2; break;
		case '3':	return 3; break;
		case '4':	return 4; break;
		case '5':	return 5; break;
		case '6':	return 6; break;
		case '7':	return 7; break;
		case '8':	return 8; break;
		case '9':	return 9; break;
		case 'a':	
		case 'A': 	return 10; break;
		case 'b':	
		case 'B':	return 11; break;
		case 'c':	
		case 'C':	return 12; break;
		case 'd':	
		case 'D':	return 13; break;
		case 'e':	
		case 'E':	return 14; break;
		case 'f':	
		case 'F':	return 15; break;
		default:
			printf("char error\n");
			exit(1);
	}
}

int dumpToBin(char *filename, WVBOX *wvbox)
{
	FILE *f;
	f = fopen(filename, "wb");
	fwrite(wvbox, sizeof(WVBOX), 1, f);
	fflush(f);
	fclose(f);	
}

int saveDeviceId(char *DeviceID)
{
	FILE *f_id;	
	f_id = fopen("DeviceId.dat", "w");
	fprintf(f_id, "%s", DeviceID);
	fflush(f_id);
	fclose(f_id);
}

int dump(WVBOX *wvbox)
{
	for(int i=0; i<strlen(wvbox->DeviceId);i++)
		printf("%c", wvbox->DeviceId[i]);
	putchar('\n');
	for(int i=0; i<16;i++)
		printf("%02x", wvbox->Key[i]);
	putchar('\n');
	for(int i=0; i<72;i++)
		printf("%02x", wvbox->ID[i]);
	putchar('\n');
	for(int i=0; i<4;i++)
		printf("%02x", wvbox->Magic[i]);
	putchar('\n');
	for(int i=0; i<4;i++)
		printf("%02x", wvbox->CRC[i]);		
	putchar('\n');
}

int readDeviceIDFromXml(const char *szFileName, WVBOX *wvbox, char *DeviceID, char *NextDeviceID)
{
	TiXmlNode *item;
	TiXmlNode* child;
	TiXmlElement *root;
	TiXmlElement *keybox;
	TiXmlAttribute *keyboxAttrib;
	const char* key;
	const char* ID;
	const char* Magic;
	const char* CRC;

	memset(wvbox, 0, sizeof(wvbox));

	TiXmlDocument doc(szFileName);
	if(!doc.LoadFile()){
		printf("Conld not load file %s, Error=%s\n", szFileName, doc.ErrorDesc());
		exit(1);
	}

#if DEBUG
	doc.Print();
#endif

	root = doc.RootElement();
	keybox = root->FirstChildElement();

#if DEBUG
	printf("Root: %s\n",root->Value());
#endif

	for (item = root->FirstChild("Keybox"); item; item = item->NextSibling( "Keybox" )){
	
	puts("___________________________________________________________________________");
		 /* read DeviceID*/
		if (strcmp("NumberOfKeyboxes", keybox->Value())==0)  /* skip unused keys */
			keybox = keybox->NextSiblingElement();
		keyboxAttrib = keybox->FirstAttribute();
		if (keyboxAttrib){
		 	printf("DeviceID:%s\n", keyboxAttrib->Value()); 	
			strcpy(wvbox->DeviceId, keyboxAttrib->Value());
		}else{
		 	printf("DeviceID: none\n");
			exit(1);  
		}

		if(strcmp(DeviceID, wvbox->DeviceId)!=0){
			keybox = keybox->NextSiblingElement();
			continue;
		}else
			keybox = keybox->NextSiblingElement();			

		if(keybox){
			keyboxAttrib = keybox->FirstAttribute();
			if(keyboxAttrib){
				strcpy(NextDeviceID, keyboxAttrib->Value());
			}else{
				printf("DeviceID has run out");
				strcmp(NextDeviceID, "EMPTY");
			}
		}
		
		for (child = item->FirstChild(); child; child = item->IterateChildren(child)){

			/* read keys */ 
			if(strcmp("Key", child->Value())==0){
				key = child->ToElement()->GetText();  
    			if (key) {  
        			printf("Key: %s\n", key);
        			for(int i=0, j=0; i<32; i=i+2){
#if DEBUG
        				printf("[%02x]", charToHex(key[i])*16+charToHex(key[i+1]));
#endif
						wvbox->Key[j] = charToHex(key[i])*16+charToHex(key[i+1]);
						j++;
        			}
    			} else {
        			printf("Key: none\n");  
					exit(1);  
    			}
			}
    		/* read ID */
			if(strcmp("ID", child->Value())==0){
				ID = child->ToElement()->GetText();  
    			if (ID) {  
        			printf("ID: %s\n", ID);
        			for(int i=0, j=0; i<144; i=i+2){
						wvbox->ID[j] = charToHex(ID[i])*16+charToHex(ID[i+1]);
						j++;
        			}
    			} else {
        			printf("ID: none\n");  
					exit(1);  
    			}
			}
			/* read Magic */
			if(strcmp("Magic", child->Value())==0){
				Magic = child->ToElement()->GetText();  
    			if (Magic) {  
        			printf("Magic: %s\n", Magic);  
        			for(int i=0, j=0; i<8; i=i+2){
						wvbox->Magic[j] = charToHex(Magic[i])*16+charToHex(Magic[i+1]);
						j++;
        			}
    			} else {
        			printf("Magic: none\n");
					exit(1);  
    			}
			}
			/* read CRC */	
			if(strcmp("CRC", child->Value())==0){
				CRC = child->ToElement()->GetText();  
    			if (CRC) {  
        			printf("CRC: %s\n", CRC);
        			for(int i=0, j=0; i<8; i=i+2){
						wvbox->CRC[j] = charToHex(CRC[i])*16+charToHex(CRC[i+1]);
						j++;
        			}        			
    			} else {
        			printf("CRC: none\n");  
					exit(1);  
				}
			}
		}
		break;
	}
}

int main(int argc, char** argv) {
	WVBOX wvbox;
	char NextDID[32];
#if DEBUG
	printf("Arg:%d\n", argc);
#endif
	
	if (argc != 7 ){
		help();
	}
	
	if (strcmp(argv[1], "-file") ==0 && strcmp(argv[3], "-key") ==0 && strcmp(argv[5], "-bin") ==0){
		readDeviceIDFromXml(argv[2], &wvbox, argv[4], NextDID);
		dumpToBin(argv[6], &wvbox);
		saveDeviceId(NextDID);
	}else{
		help();
	}
	return 0;
}
